package cn.stylefeng.guns.modular.system.service.impl;

import cn.stylefeng.guns.modular.system.controller.BixingInterfaceController;
import cn.stylefeng.guns.modular.system.dao.GameLogMapper;
import cn.stylefeng.guns.modular.system.model.GameLog;
import cn.stylefeng.guns.modular.system.model.GameUser;
import cn.stylefeng.guns.modular.system.model.Room;
import cn.stylefeng.guns.modular.system.service.IGameLogService;
import cn.stylefeng.guns.modular.system.service.IGameUserService;
import cn.stylefeng.guns.modular.system.service.IRoomService;
import cn.stylefeng.guns.modular.system.service.IScoreLevelService;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.nineshadow.games.Enum.GameConfigEnum;
import com.nineshadow.games.Enum.GameStatusEnum;
import com.nineshadow.games.Enum.GameTypeEnum;
import com.nineshadow.games.Enum.TypeEnum;
import com.nineshadow.games.constants.CommonConstants;
import com.nineshadow.games.core.GameRoom;
import com.nineshadow.games.launch.GameWorld;
import com.nineshadow.games.launch.HallServer;
import com.nineshadow.games.util.MyUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author liyong
 * @since 2019-11-28
 */
@Slf4j
@Service
public class GameLogServiceImpl extends ServiceImpl<GameLogMapper, GameLog> implements IGameLogService {

    @Autowired
    private IRoomService roomService;

    @Autowired
    private IGameUserService gameUserService;
    @Autowired
    private IScoreLevelService scoreLevelService;

    /**
     * 对手信息
     *
     * @param userId
     * @return
     */
    @Override
    public GameLog getOpponentInfo(Integer userId) throws Exception {
        GameLog gameLog = selectOne(new EntityWrapper<GameLog>()
                .eq("user_id", userId).orderBy("create_time", false).last("limit 1"));
        Assert.notNull(gameLog, "记录不存在");
        GameLog targetLog = selectOne(new EntityWrapper<GameLog>()
                .eq("room_id", gameLog.getRoomId()).notIn("user_id", userId));
        Assert.notNull(targetLog, "对手记录不存在");
        return targetLog;
    }

    @Override
    public void gameOver(String roomId, List<GameUser> users) throws Exception {
        boolean roomExist=HallServer.redisUtil.sIsmember(CommonConstants.AllROOM,roomId);
        log.info("gameOver-----roomExist:{}",roomExist);
        if (roomExist) {
            Room room = roomService.selectById(roomId);
            log.info("gameOver：--------------{}", roomId);
            //移除redis中的房间,修改房间状态
            updateRoom(roomId, room);
            String kvalue = HallServer.gameConfigService.getConfig(GameConfigEnum.K_VALUE);
            String winCoins = HallServer.gameConfigService.getConfig(GameConfigEnum.WIN_COIN);
            double k = new Double(kvalue);
            ScoreParam a = new ScoreParam();
            a.gameUser = users.get(0);
            ScoreParam b = new ScoreParam();
            b.gameUser = users.get(1);

            a.nowScore = room.getGameType() == 0 ? a.gameUser.getScore().doubleValue() : a.gameUser.getMcScore().doubleValue();
            b.nowScore = room.getGameType() == 0 ? b.gameUser.getScore().doubleValue() : b.gameUser.getMcScore().doubleValue();

            a.ea = 1.0 / (1 + Math.pow(10, (b.nowScore - a.nowScore) / 400));
            b.ea = 1.0 / (1 + Math.pow(10, (a.nowScore - b.nowScore) / 400));

            Integer user1Score = getUserScore(users.get(0).getId());
            Integer user2Score = getUserScore(users.get(1).getId());
            log.info("user1Score:{}----user2Score:{}", user1Score, user2Score);
            if (user1Score.equals(user2Score)) {
                a.s = 0.5;
                b.s = 0.5;
                a.result = 2;
                b.result = 2;
            } else if (user1Score > user2Score) {
                a.s = 1;
                a.winCoins = Integer.parseInt(winCoins);
                b.s = 0;
                a.result = 1;
                b.result = 0;
            } else {
                a.s = 0;
                b.s = 1;
                b.winCoins = Integer.parseInt(winCoins);
                a.result = 0;
                b.result = 1;
            }
            a.getScore = BigDecimal.valueOf(k * (a.s - a.ea)).setScale(2, BigDecimal.ROUND_HALF_UP);
            b.getScore = BigDecimal.valueOf(k * (b.s - b.ea)).setScale(2, BigDecimal.ROUND_HALF_UP);
            log.info("getScore: ---user1:{},user2:{}", a.getScore, b.getScore);
            saveScoreChange(roomId, a);
            saveScoreChange(roomId, b);
        }
    }

    private void updateRoom(String roomId, Room room) {
        HallServer.redisUtil.removeRoom(roomId);
        room.setEndTime(new Date());
        room.setStatus(0);
        roomService.updateById(room);
        GameRoom gameRoom=GameWorld.getRoom(roomId);
        if(Objects.nonNull(gameRoom)){
            gameRoom.terminate();
        }
    }

    /**
     * 获取用户分数
     *
     * @param userId
     * @return
     */
    public int getUserScore(Integer userId) {
        Object score = HallServer.redisUtil.hget(CommonConstants.USER + userId, CommonConstants.USER_SCORE);
        if (Objects.isNull(score)) {
            return 0;
        }
        return Integer.parseInt(score.toString());
    }

    private void saveScoreChange(String roomId, ScoreParam param) throws Exception {
        GameLog gameLog = selectOne(new EntityWrapper<GameLog>()
                .eq("room_id", roomId)
                .eq("user_id", param.gameUser.getId())
                .orderBy("create_time", false).last("limit 1"));
        Assert.notNull(gameLog, "未查到游戏记录");
        HallServer.changeLogService.saveChangeLog(gameLog.getUserId(), gameLog.getId(),
                "game_log", TypeEnum.SCORE.getCode(), 0, param.getScore, gameLog.getGameType());
        HallServer.changeLogService.saveChangeLog(gameLog.getUserId(), gameLog.getId(),
                "game_log", TypeEnum.COINS.getCode(), 0, BigDecimal.valueOf(param.winCoins), gameLog.getGameType());
        if(gameLog.getStatus()==GameStatusEnum.GAME_ING.getCdoe()){
            gameLog.setStatus(GameStatusEnum.GAME_OVER.getCdoe());
            gameLog.setIsWin(param.result);
            gameLog.setChangeCoin(param.winCoins);
            gameLog.setChangeScore(param.getScore);
            gameLog.setUpdateTime(new Date());
            updateById(gameLog);
            sendResult(roomId, param, gameLog.getGameType());
        }
    }

    public void sendResult(String roomId, ScoreParam param, int gameType) throws Exception {
        Map<String, Object> map = new HashMap<>();
        int userId = param.gameUser.getId();
        Map<String, Object> level = scoreLevelService.getUserLevel(userId, gameType);
        map.put("userId", userId);
        map.put("level", level.get("level"));
        map.put("ratio", level.get("ratio"));
        map.put("getIntegral", param.getScore);
        map.put("coins", param.winCoins);
        map.put("win", param.result);
        map.put("roomId", roomId);
        HallServer.publishService.publish(roomId,userId, MyUtils.putMsg("gameOver", map));
    }

    @Override
    public void quit(Integer userId) throws Exception {
        Object roomId = HallServer.redisUtil.hget(CommonConstants.USER + userId, CommonConstants.USER_ROOM);
        if (Objects.nonNull(roomId)) {
            HallServer.redisUtil.del(CommonConstants.AGAIN+roomId);
            GameLog gameLog=getOpponentInfo(userId);
            List<GameLog> gameLogs = selectList(new EntityWrapper<GameLog>()
                    .eq("room_id", roomId).eq("status", GameStatusEnum.GAME_ING.getCdoe()));
            Object vRoom = HallServer.redisUtil.hget(CommonConstants.USER+userId, CommonConstants.USER_VOICE_ROOM);
            //解散语音房间
            if (Objects.nonNull(vRoom)&&gameLogs.size()>0) {
                String vRoomId=vRoom.toString();
                log.info("vRoom:{}",vRoomId);
                BixingInterfaceController.removeVoiceRoom(vRoomId, GameTypeEnum.getByType(gameLogs.get(0).getGameType()));
            }
            List<Integer> userIds = gameLogs.stream().map(GameLog::getUserId).collect(Collectors.toList());
            log.info("userIds:{}",userIds.size());
            if (userIds.size() > 0) {
                log.info("{}--掉线认输---房间：{}", userId, roomId);
                //分数设置为-1,用于结算
                HallServer.redisUtil.hset(CommonConstants.USER + userId, CommonConstants.USER_SCORE, -1);
                List<GameUser> gameUsers = gameUserService.selectList(new EntityWrapper<GameUser>().in("id", userIds));
                gameOver(roomId.toString(), gameUsers);
            }
            Map<String, Object> quitMap = new HashMap<>();
            quitMap.put("userId", userId);
            HallServer.publishService.publish(roomId.toString(), gameLog.getUserId(),MyUtils.putMsg("quitRoom", quitMap));
        }
    }

    @Override
    public void updateRoomState(Integer roomId, GameStatusEnum gameStatusEnum) {
        Room room = roomService.selectById(roomId);
        if (Objects.nonNull(room) && room.getStatus() != 0) {
            switch (gameStatusEnum) {
                case GAME_ING: {
                    List<GameLog> gameLogs = selectList(new EntityWrapper<GameLog>().eq("room_id", roomId));
                    for (GameLog gameLog : gameLogs) {
                        gameLog.setStatus(GameStatusEnum.GAME_ING.getCdoe());
                        updateById(gameLog);
                    }
                    room.setStartTime(new Date());
                    roomService.updateById(room);
                    break;
                }
                case GAME_OVER: {
                    List<GameLog> gameLogs = selectList(new EntityWrapper<GameLog>().eq("room_id", roomId));
                    for (GameLog gameLog : gameLogs) {
                        gameLog.setStatus(GameStatusEnum.GAME_OVER.getCdoe());
                        updateById(gameLog);
                    }
                    room.setStatus(0);
                    roomService.updateById(room);
                    break;
                }
                default:
                    break;
            }
        }
    }

    private static class ScoreParam {
        private GameUser gameUser;
        private Double nowScore;
        private double ea;
        private double s;
        private BigDecimal getScore;
        private int winCoins;
        private int result;
    }
}
